package com.ev.mes.service.impl;

import com.ev.apis.model.DsResultResponse;
import com.ev.custom.dao.DeviceDao;
import com.ev.custom.domain.DeviceDO;
import com.ev.framework.il8n.MessageSourceHandler;
import com.ev.framework.utils.BeanUtils;
import com.ev.framework.utils.ListUtils;
import com.ev.framework.utils.R;
import com.ev.framework.utils.StringUtils;
import com.ev.mes.dao.CrewSchedulingDao;
import com.ev.mes.dao.CrewSchedulingItemDao;
import com.ev.mes.dao.ShiftDao;
import com.ev.mes.domain.CrewSchedulingDO;
import com.ev.mes.domain.CrewSchedulingItemDO;
import com.ev.mes.domain.ShiftDO;
import com.ev.mes.service.CrewSchedulingService;
import com.ev.mes.vo.CrewSchedulingItemVO;
import com.ev.mes.vo.CrewSchedulingPageParam;
import com.ev.mes.vo.CrewSchedulingSaveParam;
import com.ev.mes.vo.ShiftVO;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@Service
public class CrewSchedulingServiceImpl implements CrewSchedulingService {
    @Autowired
    private CrewSchedulingDao crewSchedulingDao;
    @Autowired
    private CrewSchedulingItemDao crewSchedulingItemDao;
    @Autowired
    private MessageSourceHandler messageSourceHandler;
    @Autowired
    private ShiftDao shiftDao;
    @Autowired
    private DeviceDao deviceDao;

    @Override
    public CrewSchedulingDO get(Long id) {
        return crewSchedulingDao.get(id);
    }

    @Override
    public List<CrewSchedulingDO> list(Map<String, Object> map) {
        return crewSchedulingDao.list(map);
    }

    @Override
    public int count(Map<String, Object> map) {
        return crewSchedulingDao.count(map);
    }

    @Override
    public int save(CrewSchedulingDO crewScheduling) {
        return crewSchedulingDao.save(crewScheduling);
    }

    @Override
    public int update(CrewSchedulingDO crewScheduling) {
        return crewSchedulingDao.update(crewScheduling);
    }

    @Override
    public int updateAll(CrewSchedulingDO crewScheduling) {
        return crewSchedulingDao.updateAll(crewScheduling);
    }

    @Override
    public int remove(Long id) {
        return crewSchedulingDao.remove(id);
    }

    @Override
    public int batchRemove(Long[] ids) {
        return crewSchedulingDao.batchRemove(ids);
    }

    @Override
    public R saveAndVerify(CrewSchedulingSaveParam saveParam) {
        CrewSchedulingDO crewSchedulingDO = saveParam.getCrewSchedulingDO();
        List<CrewSchedulingItemDO> crewSchedulingItemDOS = saveParam.getCrewSchedulingItemDOS();
        List<Long> deviceIdList = crewSchedulingItemDOS.stream().map(CrewSchedulingItemDO::getDeviceId).distinct().collect(Collectors.toList());
        // 若有重复的设备则不能添加
        if (deviceIdList.size() != crewSchedulingItemDOS.size()){
            List<Long> deviceIds = crewSchedulingItemDOS.stream().map(CrewSchedulingItemDO::getDeviceId).collect(Collectors.toList());

            List<Long> duplicateElements = ListUtils.getDuplicateElements(deviceIds);
            Map<String, Object> param = Maps.newHashMap();
            param.put("deviceIdList", duplicateElements);
            List<DeviceDO> deviceDOS = deviceDao.list(param);
            String[] args = {deviceDOS.stream().map(DeviceDO::getName).collect(Collectors.joining("、"))};
            return R.error(messageSourceHandler.getMessage("mes.device.isRepeat", args));
        }

        Long id = crewSchedulingDO.getId();
        boolean isUpdate = id != null;
        Date startTime = crewSchedulingDO.getStartTime();
        Date now = new Date();
        if (isUpdate) {
            CrewSchedulingDO crewScheduling = this.get(id);
            // 若开始时间超过现在则不修改主表  只修改子表的停用状态
            if (crewScheduling.getStartTime().before(now)) {
                List<CrewSchedulingItemDO> stopList = crewSchedulingItemDOS.stream().filter(e -> e.getId() != null && e.getIsUse() == 0 && e.getEndTime() == null).collect(Collectors.toList());
                if (stopList.size() > 0) {
                    stopList.forEach(e -> e.setEndTime(now));
                    crewSchedulingItemDao.batchUpdate(stopList);
                }
            } else {
                if (startTime.before(now)) {
                    return R.error(messageSourceHandler.getMessage("mes.time.isPast", null));
                }
                // 验证班次选择是否有误
                R checkTime = this.checkTime(crewSchedulingItemDOS);
                if (checkTime != null) {
                    return checkTime;
                }
                crewSchedulingItemDOS.forEach(e -> {
                    e.setIsUse(1);
                    e.setStartTime(startTime);
                });
                List<CrewSchedulingItemDO> saveList = crewSchedulingItemDOS.stream().filter(e -> e.getId() == null).collect(Collectors.toList());
                // 检验设备是否有重复的排班计划
                R checkRepeat = this.checkRepeat(crewSchedulingItemDOS);
                if (checkRepeat != null) {
                    return checkRepeat;
                }

                this.update(crewSchedulingDO);
                List<CrewSchedulingItemDO> updateList = crewSchedulingItemDOS.stream().filter(e -> e.getId() != null).collect(Collectors.toList());
                for (CrewSchedulingItemDO e : saveList) {
                    e.setCrewId(id);
                }
                // 保存新增的
                if (saveList.size() > 0) {
                    crewSchedulingItemDao.batchSave(saveList);
                }
                // 删除Id
                List<Long> crewIds = saveParam.getCrewIds();
                if (crewIds!=null && crewIds.size() > 0) {
                    crewSchedulingItemDao.batchRemove(crewIds.toArray(new Long[0]));
                }
                // 保存新增的
                if (updateList.size() > 0) {
                    crewSchedulingItemDao.batchUpdate(updateList);
                }
            }
        }

        // 新增
        if (!isUpdate) {
            // 开始时间不能超过现在
            if (startTime.before(now)) {
                return R.error(messageSourceHandler.getMessage("mes.time.isPast", null));
            }
            // 验证班次选择是否有误
            R checkTime = this.checkTime(crewSchedulingItemDOS);
            if (checkTime != null) {
                return checkTime;
            }
            crewSchedulingItemDOS.forEach(e -> {
                e.setIsUse(1);
                e.setStartTime(startTime);
            });
            // 检验设备是否有重复的排班计划
            R checkRepeat = this.checkRepeat(crewSchedulingItemDOS);
            if (checkRepeat != null) {
                return checkRepeat;
            }
            this.save(crewSchedulingDO);
            id = crewSchedulingDO.getId();
            for (CrewSchedulingItemDO e : crewSchedulingItemDOS) {
                e.setCrewId(id);
            }
            crewSchedulingItemDao.batchSave(crewSchedulingItemDOS);
        }
        Map<String, Object> result = Maps.newHashMap();
        result.put("id", id);
        return R.ok(result);
    }

    private R checkRepeat(List<CrewSchedulingItemDO> crewSchedulingItemDOS) {
        for (int i = 0; i < crewSchedulingItemDOS.size(); i++) {
            CrewSchedulingItemDO crewSchedulingItemDO = crewSchedulingItemDOS.get(i);
            // 已存在的排班计划
            List<CrewSchedulingItemVO> crewSchedulingItemVOS = crewSchedulingItemDao.checkRepeat(crewSchedulingItemDO);
            if (crewSchedulingItemVOS.size() > 0) {
                String[] args = {"第" + (i+1) + "行"};
                return R.error(messageSourceHandler.getMessage("mes.device.time.isRepeat", args));
            }
           /* if (crewSchedulingItemVOS.size() == 0) {
                continue;
            }
            crewSchedulingItemVOS.sort(Comparator.comparing(CrewSchedulingItemVO::getStartSec));
            long end = crewSchedulingItemVOS.get(0).getEndSec();
            for (int j = 1; j < crewSchedulingItemVOS.size(); j++) {
                if (crewSchedulingItemVOS.get(j).getStartSec() < end) {
                    String[] args = {"第" + (i+1) + "行"};
                    return R.error(messageSourceHandler.getMessage("mes.device.time.isRepeat", args));
                }
                end = crewSchedulingItemVOS.get(j).getEndSec();
            }*/
        }

        return null;
    }

    private R checkTime(List<CrewSchedulingItemDO> crewSchedulingItemDOS) {
        ShiftVO shiftVO;
        for (int i = 0; i < crewSchedulingItemDOS.size(); i++) {
            CrewSchedulingItemDO crewSchedulingItemDO = crewSchedulingItemDOS.get(i);
            String shiftIds = crewSchedulingItemDO.getShiftIds();
            List<Long> shiftIdList = Lists.newArrayList(shiftIds.split(",")).stream().filter(StringUtils::isNoneEmpty).map(Long::parseLong).collect(Collectors.toList());
            List<ShiftVO> shiftVOS = shiftDao.getByShiftIdList(shiftIdList);
            // 找出跨天的 结束时间小于开始时间
            List<ShiftVO> crossDaysList = shiftVOS.stream().filter(e -> e.getEndSec() < e.getStartSec()).collect(Collectors.toList());
            // 若有1个以上跨天的则不成立
            if (crossDaysList.size() > 1) {
                String[] args = {"第" + (i+1) + "行"};
                return R.error(messageSourceHandler.getMessage("mes.time.isCross", args));
            }
            // 若存在跨天则分裂为两个数据
            if (crossDaysList.size() == 1) {
                shiftVOS.removeAll(crossDaysList);
                long startSec = crossDaysList.get(0).getStartSec();
                long endSec = crossDaysList.get(0).getEndSec();
                shiftVO = new ShiftVO();
                BeanUtils.copyProperties(crossDaysList.get(0),shiftVO);
                crossDaysList.add(shiftVO);
                crossDaysList.get(0).setStartSec(0);
                crossDaysList.get(0).setEndSec(endSec);
                crossDaysList.get(1).setStartSec(startSec);
                crossDaysList.get(1).setEndSec(86400);
                shiftVOS.addAll(crossDaysList);
            }
            shiftVOS.sort(Comparator.comparing(ShiftVO::getStartSec));
            // PS:|_______|为时间段
            // 例：0:|_______|
            //    1:   |_______|

            // 例：0:|_______|
            //    1:          |_______|
            //    2:                  |_______|（通过）
            long end = shiftVOS.get(0).getEndSec();
            for (int j = 1; j < shiftVOS.size(); j++) {
                if (shiftVOS.get(j).getStartSec() < end) {
                    String[] args = {"第" + (i+1) + "行"};
                    return R.error(messageSourceHandler.getMessage("mes.time.isCross", args));
                }
                end = shiftVOS.get(j).getEndSec();
            }
        }
        return null;
    }

    @Override
    public Map<String, Object> getById(Long id) {
        Map<String, Object> crewScheduling = crewSchedulingDao.getById(id);
        Map<String, Object> result = Maps.newHashMap();
        if (crewScheduling != null) {
            result.put("headInfo", crewScheduling);
            List<Map<String, Object>> itemInfo = crewSchedulingItemDao.getByCrewId(id);
            if (itemInfo.size() > 0) {
                this.setShiftInfo(itemInfo);
                result.put("itemInfo", itemInfo);
            }
            return result;
        }
        return result;
    }

    private void setShiftInfo(List<Map<String, Object>> itemInfo) {
        String shiftIds = itemInfo.stream().map(e -> e.get("shiftIds").toString()).collect(Collectors.joining(","));
        List<Long> shiftIdList = Stream.of(shiftIds.split(",")).filter(StringUtils::isNoneEmpty).distinct().map(Long::parseLong).collect(Collectors.toList());
        Map<String, Object> param = Maps.newHashMap();
        param.put("shiftIdList", shiftIdList);
        List<ShiftDO> shiftDOList = shiftDao.list(param);
        Map<Long, String> shiftMap = shiftDOList.stream().collect(Collectors.toMap(ShiftDO::getId, ShiftDO::getName));
        itemInfo.forEach(e -> {
            boolean isUse = Objects.equals("1", e.get("isUse").toString());
            e.put("isUseName", isUse ? "启用" : "停用");
            String shift = e.get("shiftIds").toString();
            List<String> shiftToList = Lists.newArrayList(shift.split(","));
            shiftToList.removeIf(String::isEmpty);
            e.put("shiftIds", shiftToList.stream().map(Long::parseLong).collect(Collectors.toList()));
            shiftToList.replaceAll(s -> shiftMap.get(Long.parseLong(s)));
            String shiftIdsName = StringUtils.join(shiftToList.toArray(), ",");
            e.put("shiftIdsName", shiftIdsName);



        });
    }

    @Override
    public Map<String, Object> pageList(CrewSchedulingPageParam pageParam) {
        Map<String, Object> result = Maps.newHashMap();
        List<Map<String, Object>> data = crewSchedulingDao.pageList(pageParam);
        if (data.size() > 0) {
            this.setShiftInfo(data);
            int total = crewSchedulingDao.pageCount(pageParam);
            result.put("data", new DsResultResponse(pageParam.getPageno(), pageParam.getPagesize(), total, data));
        }
        return result;
    }

    @Override
    public R batchDelete(List<Long> ids) {
        Map<String, Object> param = Maps.newHashMap();
        param.put("ids", ids);
        List<CrewSchedulingDO> crewSchedulingDOS = this.list(param);
        Date now = new Date();
        String error = crewSchedulingDOS.stream().filter(e -> e.getStartTime().before(now)).map(CrewSchedulingDO::getName).collect(Collectors.joining(","));
        if (StringUtils.isNoneEmpty(error)) {
            String[] args = {error};
            return R.error(messageSourceHandler.getMessage("mes.time.isStart", args));
        }
        Long[] crewIds = ids.toArray(new Long[0]);
        this.batchRemove(crewIds);
        crewSchedulingItemDao.batchRemoveByCrewIds(crewIds);
        return R.ok();
    }

}
